package server;

import common.LogUtil;
import common.Message;
import common.Protocol;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 用户连接实现
 */
public class UserConnection {
    private static final Logger logger = LogUtil.getLogger(UserConnection.class.getName());

    // 关联的代理管理器
    private final ProxyManager proxyManager;

    // 关联的代理服务
    private final ProxyService proxyService;

    // 连接ID
    private final int connectionId;

    // 用户通道
    private final SocketChannel userChannel;

    // 通信缓冲区 - 使用直接缓冲区提高性能
    private final ByteBuffer buffer = ByteBuffer.allocateDirect(Protocol.BUFFER_SIZE);

    // 选择器
    private Selector selector;

    // 客户端处理器 - 选择的客户端
    private ClientHandler clientHandler;

    // 运行标志
    private volatile boolean running = false;

    // 协议类型
    private final String protocolType;

    // 是否打印传输内容
    private final boolean printContent;

    // 统计信息
    private final AtomicLong bytesReceived = new AtomicLong(0);
    private final AtomicLong bytesSent = new AtomicLong(0);
    private final long creationTime = System.currentTimeMillis();

    // 内容处理相关的正则表达式对象，预编译以提高性能
    private static final Pattern HTTP_REQUEST_PATTERN =
            Pattern.compile("^(GET|POST|PUT|DELETE|HEAD|OPTIONS|PATCH) ([^ ]+) HTTP/\\d\\.\\d");
    private static final Pattern HTTP_RESPONSE_PATTERN =
            Pattern.compile("^HTTP/\\d\\.\\d (\\d+) (.*)");
    private static final Pattern HTTP_HOST_PATTERN =
            Pattern.compile("Host: ([^\\r\\n]+)");
    private static final Pattern HTTP_CONTENT_TYPE_PATTERN =
            Pattern.compile("Content-Type: ([^\\r\\n]+)");
    private static final Pattern HTTP_CONTENT_LENGTH_PATTERN =
            Pattern.compile("Content-Length: (\\d+)");

    // 空间计数器 - 用于限制日志频率
    private int logCounter = 0;
    private static final int LOG_INTERVAL = 100; // 每100个数据包记录一次详细日志

    /**
     * 构造函数
     */
    public UserConnection(ProxyManager proxyManager, ProxyService proxyService,
                          int connectionId, SocketChannel userChannel) {
        this.proxyManager = proxyManager;
        this.proxyService = proxyService;
        this.connectionId = connectionId;
        this.userChannel = userChannel;

        // 获取协议类型和内容打印设置
        this.protocolType = proxyService.getConfig().getProtocol();

        // 读取服务器配置中是否启用内容打印
        // 默认为true，实际使用时可以从配置中获取
        this.printContent = true;
    }

    /**
     * 开始处理连接
     */
    public void start() throws IOException {
        if (running) {
            return;
        }

        // 配置通道
        userChannel.configureBlocking(false);

        // 初始化选择器
        selector = Selector.open();
        userChannel.register(selector, SelectionKey.OP_READ);

        // 设置运行标志
        running = true;

        // 选择客户端处理器
        selectClientHandler();

        // 开始数据传输
        proxyManager.getServer().getExecutor().execute(this::processData);

        logger.info("Started user connection " + connectionId + " with protocol: " + protocolType);
    }

    /**
     * 选择客户端处理器
     */
    private void selectClientHandler() {
        // 找到一个认证通过的客户端
        ClientManager clientManager = proxyManager.getServer().getClientManager();

        // 简单实现：选择第一个认证通过的客户端
        for (String clientId : clientManager.getClients().keySet()) {
            ClientHandler handler = clientManager.getClient(clientId);
            if (handler != null && handler.isAuthenticated()) {
                clientHandler = handler;
                break;
            }
        }

        if (clientHandler == null) {
            logger.warning("No authenticated client available for connection: " + connectionId);
            close();
            return;
        }

        // 通知客户端有新连接
        try {
            Message newConnMsg = Message.newConnection(connectionId);
            clientHandler.sendMessage(newConnMsg);

            logger.info("New connection notification sent to client: " + clientHandler.getClientId() +
                    " for connection: " + connectionId);
        } catch (IOException e) {
            logger.log(Level.SEVERE, "Error sending new connection notification", e);
            close();
        }
    }

    /**
     * 处理数据传输
     */
    private void processData() {
        long lastActivityTime = System.currentTimeMillis();
        final long IDLE_TIMEOUT = 300_000; // 5分钟超时

        try {
            while (running && userChannel.isOpen()) {
                // 使用较短的超时时间以便定期检查连接状态
                int selected = selector.select(1000);

                long currentTime = System.currentTimeMillis();

                if (selected > 0) {
                    processSelectedKeys();
                    lastActivityTime = currentTime;
                } else if (currentTime - lastActivityTime > IDLE_TIMEOUT) {
                    // 如果长时间没有活动，关闭连接
                    logger.info("Connection " + connectionId + " idle timeout after "
                            + (IDLE_TIMEOUT / 1000) + " seconds");
                    break;
                }
            }
        } catch (Exception e) {
            logger.log(Level.WARNING, "Error processing data for connection: " + connectionId, e);
        } finally {
            close();
        }
    }

    /**
     * 处理选择器事件
     */
    private void processSelectedKeys() throws IOException {
        for (SelectionKey key : selector.selectedKeys()) {
            if (!key.isValid()) {
                continue;
            }

            if (key.isReadable()) {
                readFromUser();
            }

            selector.selectedKeys().clear();
        }
    }

    /**
     * 从用户通道读取数据
     */
    private void readFromUser() throws IOException {
        buffer.clear();
        int bytesRead;

        try {
            bytesRead = userChannel.read(buffer);
        } catch (IOException e) {
            // 捕获特定的IO异常，处理连接重置等问题
            logger.log(Level.FINE, "Connection " + connectionId + " read error: " + e.getMessage());
            close();
            return;
        }

        if (bytesRead == -1) {
            // 连接关闭
            close();
            return;
        }

        if (bytesRead > 0) {
            bytesReceived.addAndGet(bytesRead);
            buffer.flip();

            // 优化: 避免不必要的数组复制，直接从ByteBuffer传输数据
            if (clientHandler != null) {
                if (printContent) {
                    // 只有需要打印内容时才复制数据
                    byte[] data = new byte[buffer.remaining()];
                    // 记住当前位置
                    int pos = buffer.position();
                    buffer.get(data);
                    // 重置位置，以便后续操作可以重新读取数据
                    buffer.position(pos);

                    // 有选择地打印内容，防止日志过多
                    if (shouldLog()) {
                        printTransferContent(data, true);
                    }
                }

                // 创建消息并发送
                // 注意: 我们直接从buffer创建消息，而不是从复制的数组
                ByteBuffer msgBuffer = ByteBuffer.allocate(buffer.remaining() + 4);
                msgBuffer.putInt(connectionId);
                msgBuffer.put(buffer);
                msgBuffer.flip();

                Message dataMsg = new Message(Protocol.MSG_TYPE_DATA, msgBuffer.array());
                clientHandler.sendMessage(dataMsg);
            } else {
                logger.warning("No client handler available for connection: " + connectionId);
            }
        }
    }

    /**
     * 判断是否应该记录详细日志
     */
    private boolean shouldLog() {
        logCounter++;
        return logCounter % LOG_INTERVAL == 0 || bytesReceived.get() < 10000; // 前10KB或每隔一段传输记录
    }

    /**
     * 向用户通道写入数据
     */
    public void writeToUser(byte[] data) throws IOException {
        if (!userChannel.isOpen()) {
            return;
        }

        // 解析并打印传输内容
        if (printContent && shouldLog()) {
            printTransferContent(data, false);
        }

        // 使用更高效的直接缓冲区
        ByteBuffer writeBuffer = ByteBuffer.allocateDirect(data.length);
        writeBuffer.put(data).flip();

        int totalWritten = 0;
        int retryCount = 0;
        final int MAX_RETRIES = 3;

        // 添加写入重试逻辑和完整性检查
        while (writeBuffer.hasRemaining()) {
            int bytesWritten;

            try {
                bytesWritten = userChannel.write(writeBuffer);
            } catch (IOException e) {
                if (retryCount < MAX_RETRIES) {
                    // 短暂等待后重试
                    retryCount++;
                    try {
                        Thread.sleep(10);
                        continue;
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        throw e; // 重新抛出原始IOException
                    }
                } else {
                    throw e; // 超过重试次数，抛出异常
                }
            }

            if (bytesWritten == 0 && retryCount < MAX_RETRIES) {
                // 通道暂时不可写，短暂等待后重试
                retryCount++;
                try {
                    Thread.sleep(10);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                }
            } else if (bytesWritten > 0) {
                totalWritten += bytesWritten;
                retryCount = 0; // 重置重试计数
            }
        }

        bytesSent.addAndGet(totalWritten);
    }

    /**
     * 解析并打印传输内容
     *
     * @param data 传输的数据
     * @param isFromUser 是否是从用户到服务的数据
     */
    private void printTransferContent(byte[] data, boolean isFromUser) {
        // 避免处理非常大的数据包，只取前部分进行分析
        byte[] sampleData = data;
        if (data.length > 4096) {
            sampleData = new byte[4096];
            System.arraycopy(data, 0, sampleData, 0, 4096);
        }

        String direction = isFromUser ? "User → Server" : "Server → User";
        String prefix = "[Conn: " + connectionId + "] " + direction + " [" + protocolType + "]: ";

        // 添加传输统计信息
        long totalReceived = bytesReceived.get();
        long totalSent = bytesSent.get();
        long duration = (System.currentTimeMillis() - creationTime) / 1000;
        String stats = String.format("Total: %d bytes ↓ %d bytes ↑ in %ds",
                totalReceived, totalSent, duration);

        // 根据协议类型使用缓存机制减少重复解析
        try {
            switch (protocolType.toUpperCase()) {
                case "HTTP":
                    printHttpContent(sampleData, prefix + " " + stats);
                    break;

                case "TCP":
                    printTcpContent(sampleData, prefix + " " + stats);
                    break;

                case "BINARY":
                    printBinaryContent(sampleData, prefix + " " + stats);
                    break;

                default:
                    // 对于未知类型，打印简单摘要
                    printDefaultContent(sampleData, prefix + " " + stats);
                    break;
            }
        } catch (Exception e) {
            // 防止内容解析影响主要逻辑
            logger.log(Level.FINE, "Error parsing content: " + e.getMessage());
            printDefaultContent(sampleData, prefix + " (Parse error) " + stats);
        }
    }

    /**
     * 打印HTTP协议内容
     */
    private void printHttpContent(byte[] data, String prefix) {
        // 避免再次转换为字符串，使用缓存的数据
        try {
            String content = new String(data, StandardCharsets.UTF_8);

            // 找到第一行的结束位置
            int firstLineEnd = content.indexOf('\n');
            if (firstLineEnd == -1) {
                // 如果没有换行符，可能是不完整的请求
                printDefaultContent(data, prefix + "(Incomplete HTTP)");
                return;
            }

            String firstLine = content.substring(0, firstLineEnd).trim();

            StringBuilder logMessage = new StringBuilder();

            // 尝试匹配HTTP请求头
            Matcher requestMatcher = HTTP_REQUEST_PATTERN.matcher(firstLine);
            if (requestMatcher.find()) {
                String method = requestMatcher.group(1);
                String path = requestMatcher.group(2);

                // 构建更紧凑的日志输出
                logMessage.append("HTTP Request: ").append(method).append(" ").append(path);

                // 提取Host头
                Matcher hostMatcher = HTTP_HOST_PATTERN.matcher(content);
                if (hostMatcher.find()) {
                    logMessage.append(" | Host: ").append(hostMatcher.group(1));
                }

                // 如果是POST/PUT请求，可以提取Content-Type和可能的Body
                if ("POST".equals(method) || "PUT".equals(method)) {
                    Matcher contentTypeMatcher = HTTP_CONTENT_TYPE_PATTERN.matcher(content);
                    if (contentTypeMatcher.find()) {
                        logMessage.append(" | Type: ").append(contentTypeMatcher.group(1));
                    }

                    // 查找Content-Length
                    Matcher contentLengthMatcher = HTTP_CONTENT_LENGTH_PATTERN.matcher(content);
                    if (contentLengthMatcher.find()) {
                        logMessage.append(" | Length: ").append(contentLengthMatcher.group(1));
                    }

                    // 打印简短的Body摘要 - 只在一定大小下
                    int bodyStart = content.indexOf("\r\n\r\n");
                    if (bodyStart == -1) {
                        bodyStart = content.indexOf("\n\n");
                    }

                    if (bodyStart != -1 && content.length() > bodyStart + 4) {
                        String body = content.substring(bodyStart + 4);
                        if (!body.isEmpty()) {
                            logMessage.append(" | Body: ")
                                    .append(body.length() > 50 ? body.substring(0, 50) + "..." : body);
                        }
                    }
                }

                logger.info(prefix + logMessage.toString());
                return;
            }

            // 尝试匹配HTTP响应头
            Matcher responseMatcher = HTTP_RESPONSE_PATTERN.matcher(firstLine);
            if (responseMatcher.find()) {
                String statusCode = responseMatcher.group(1);
                String statusMessage = responseMatcher.group(2);

                // 构建紧凑的响应日志
                logMessage.append("HTTP Response: ").append(statusCode).append(" ").append(statusMessage);

                // 提取Content-Type
                Matcher contentTypeMatcher = HTTP_CONTENT_TYPE_PATTERN.matcher(content);
                if (contentTypeMatcher.find()) {
                    logMessage.append(" | Type: ").append(contentTypeMatcher.group(1));
                }

                // 提取Content-Length
                Matcher contentLengthMatcher = HTTP_CONTENT_LENGTH_PATTERN.matcher(content);
                if (contentLengthMatcher.find()) {
                    logMessage.append(" | Length: ").append(contentLengthMatcher.group(1));
                }

                logger.info(prefix + logMessage.toString());
                return;
            }

            // 如果不是完整的HTTP请求/响应，则按默认方式处理
            printDefaultContent(data, prefix + "(Partial HTTP)");

        } catch (Exception e) {
            // 降级为默认内容处理
            logger.log(Level.FINE, "Error parsing HTTP content: " + e.getMessage());
            printDefaultContent(data, prefix);
        }
    }

    /**
     * 打印TCP协议内容
     */
    private void printTcpContent(byte[] data, String prefix) {
        // 首先尝试以UTF-8解码，如果包含不可打印字符太多则按二进制处理
        try {
            String content = new String(data, StandardCharsets.UTF_8);

            // 检查是否大部分是可打印字符
            int printableChars = 0;
            for (char c : content.toCharArray()) {
                if (c >= 32 && c < 127) {
                    printableChars++;
                }
            }

            double printableRatio = (double) printableChars / content.length();

            if (printableRatio > 0.8) {
                // 大部分是可打印字符，按文本处理
                if (content.length() > 100) {
                    logger.info(prefix + "Text data (" + data.length + " bytes): " +
                            content.substring(0, 100) + "...");
                } else {
                    logger.info(prefix + "Text data: " + content);
                }
            } else {
                // 按二进制处理
                printBinaryContent(data, prefix);
            }
        } catch (Exception e) {
            logger.log(Level.WARNING, "Error parsing TCP content", e);
            printBinaryContent(data, prefix);
        }
    }

    /**
     * 打印二进制内容
     */
    private void printBinaryContent(byte[] data, String prefix) {
        StringBuilder hexOutput = new StringBuilder();
        StringBuilder asciiOutput = new StringBuilder();

        int maxShow = Math.min(32, data.length); // 最多显示32字节

        for (int i = 0; i < maxShow; i++) {
            byte b = data[i];

            // 添加十六进制表示
            String hex = String.format("%02X ", b & 0xFF);
            hexOutput.append(hex);

            // 添加ASCII表示
            if (b >= 32 && b < 127) {
                asciiOutput.append((char) b);
            } else {
                asciiOutput.append('.');
            }

            // 每8个字节添加一个空格
            if ((i + 1) % 8 == 0) {
                hexOutput.append(" ");
                asciiOutput.append(" ");
            }
        }

        // 如果有更多数据，添加省略号
        if (data.length > maxShow) {
            hexOutput.append("...");
        }

        logger.info(prefix + "Binary data (" + data.length + " bytes)");
        logger.info(prefix + "HEX: " + hexOutput.toString());
        logger.info(prefix + "ASCII: " + asciiOutput.toString());
    }

    /**
     * 打印默认内容格式
     */
    private void printDefaultContent(byte[] data, String prefix) {
        logger.info(prefix + "Data transfer (" + data.length + " bytes)");

        // 尝试打印可能的文本内容
        try {
            String preview = new String(data, 0, Math.min(50, data.length), StandardCharsets.UTF_8)
                    .replaceAll("[\\r\\n]", " ")
                    .trim();

            if (!preview.isEmpty()) {
                logger.info(prefix + "Preview: " + preview +
                        (data.length > 50 ? "..." : ""));
            }
        } catch (Exception e) {
            // 忽略解码错误
        }
    }

    /**
     * 关闭连接
     */
    public void close() {
        if (!running) {
            return;
        }

        running = false;

        try {
            // 记录连接统计信息
            long duration = (System.currentTimeMillis() - creationTime) / 1000;
            logger.info(String.format(
                    "Connection %d stats: Active for %ds, Received: %d bytes, Sent: %d bytes, Protocol: %s",
                    connectionId, duration, bytesReceived.get(), bytesSent.get(), protocolType
            ));

            // 通知客户端连接关闭 - 确保只尝试一次，避免阻塞
            if (clientHandler != null) {
                try {
                    Message closeMsg = Message.closeConnection(connectionId);
                    clientHandler.sendMessage(closeMsg);
                } catch (IOException e) {
                    // 只记录错误，不阻止关闭过程
                    logger.log(Level.FINE, "Error sending close notification: " + e.getMessage());
                }
            }

            // 关闭资源 - 使用try-with-resources确保正确关闭
            try {
                // 关闭通道
                if (userChannel != null && userChannel.isOpen()) {
                    userChannel.close();
                }
            } finally {
                try {
                    // 关闭选择器
                    if (selector != null && selector.isOpen()) {
                        selector.close();
                    }
                } finally {
                    // 从管理器中移除 - 确保即使发生异常也会执行
                    proxyManager.removeUserConnection(connectionId);
                }
            }

            logger.info("User connection closed: " + connectionId);
        } catch (Exception e) {
            // 捕获所有异常，确保关闭过程能够完成
            logger.log(Level.WARNING, "Error during connection closure: " + connectionId, e);
        }
    }

    /**
     * 获取连接统计信息
     */
    public String getStatistics() {
        long duration = (System.currentTimeMillis() - creationTime) / 1000;
        return String.format(
                "Connection %d: Active for %ds, Received: %d bytes, Sent: %d bytes, Protocol: %s",
                connectionId, duration, bytesReceived.get(), bytesSent.get(), protocolType
        );
    }

    /**
     * 获取连接ID
     */
    public int getConnectionId() {
        return connectionId;
    }

    /**
     * 获取用户通道
     */
    public SocketChannel getUserChannel() {
        return userChannel;
    }

    /**
     * 获取代理服务
     */
    public ProxyService getProxyService() {
        return proxyService;
    }
}
